/******************************************************************************
 * Copyright (C) 2011 ShenZhen ComTop Information Technology Co.,Ltd
 * All Rights Reserved.
 * 本软件为深圳康拓普开发研制。未经本公司正式书面同意，其他任何个人、团体不得使用、
 * 复制、修改或发布本软件.
 *****************************************************************************/

package com.code.common.excel.officeexport.excel.process;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

import com.code.common.excel.officeexport.excel.exception.ExportExcelException;
import com.code.common.excel.officeexport.excel.help.ExcelExportConstants;
import com.code.common.excel.officeexport.excel.help.ExportCellFormat;
import com.code.common.excel.officeexport.excel.tag.ExcelCell;
import com.code.common.excel.officeexport.excel.tag.ExcelUtil;
import com.code.common.excel.officeexport.excel.tag.TageList;
import com.code.common.excel.officeexport.excel.tag.TageObject;
import com.code.common.excel.officeexport.excel.tag.TageSum;

/**
 * excel导出处理类
 * 
 * 重构点：字段是list但是里面装的东西要确定为一种类型
 * 
 * @author 李鹏
 * @since 1.0
 * @version 2012-11-09 李鹏 新建
 */
public class ExportPrinter {

	/** 日志管理类对象 */
	private static final Logger LOGGER = Logger.getLogger(ExportPrinter.class);

	/** WritableWorkbook */
	private HSSFWorkbook writableWorkbook = null;

	/** Excel模版文件所在目录 */
	public static String TEMPLATES_DIRECTORY = System.getProperty("webRoot.root")
			+ "export"+File.separator+"templates" + File.separator;

	/** 数据列表 */
	private final List<Object> data;

	/** 分页数量 */
	private int pageSize = 0;

	/** 配置信息 */
	private final String fileName;

	/** 配置信息 */
	private final String templateFile;

	/** 格式化 */
	private ExportCellFormat exportCellFormat;

	/** sheet */
	private HSSFSheet writableSheet = null;

	private String sysId = null;

	private String filePath = null;

	/** 记录分页标记 */
	// final private Map<String, Object> breakMap;// = new HashMap();

	/** 模板sheet的名称,以供删除excel中这些模板使用 */
	private final Set<String> delSheetName;

	/**
	 * 构造函数
	 * 
	 * @param excel List
	 * @param excelExportConfig 导出配置
	 */
	@SuppressWarnings("unchecked")
	public ExportPrinter(List<Object> excel, String fileName, String templateFile) {
		this.data = (List<Object>) excel.get(2);
		// this.breakMap = (Map<String, Object>) excel.get(3);
		this.delSheetName = new HashSet<String>();
		this.fileName = fileName;
		this.templateFile = templateFile;
		this.sysId = UUID.randomUUID().toString();
		this.filePath = TEMPLATES_DIRECTORY + sysId + File.separator;

	}

	/**
	 * 构造函数
	 * 
	 * @param excel List
	 * @param excelExportConfig 导出配置
	 */
	public ExportPrinter(String fileName) {
		this.data = null;
		// this.breakMap = null;
		this.delSheetName = null;
		this.fileName = fileName;
		this.templateFile = null;
		this.sysId = UUID.randomUUID().toString();
		this.filePath = TEMPLATES_DIRECTORY + sysId + File.separator;

	}

	/**
	 * 输出Excel
	 * 
	 * @param workbook 模板workbook
	 * @param os 输出流
	 * @throws IOException 出错时抛出异常
	 */
	public void writeObject(HSSFWorkbook workbook) throws IOException {
		FileOutputStream objFileOutputStream = null;
		try {
			// 创建Excel文件
			this.exportCellFormat = new ExportCellFormat(this.templateFile);
			this.initWorkbook();
			this.writeLine(writableWorkbook.getSheetName(0), 0, false);
			objFileOutputStream = new FileOutputStream(this.filePath + this.fileName
					+ ExcelExportConstants.EXPORT_EXCEL_2003);
			writableWorkbook.write(objFileOutputStream);
		} catch (Exception e) {
			LOGGER.error("ExportPrinter输出Excel出错：" + e.getMessage());
		} finally {
			if (objFileOutputStream != null) {
				objFileOutputStream.close();
				objFileOutputStream = null;
			}
		}
	}
	
	/**
	 * 输出Excel
	 * 
	 * @param workbook 模板workbook
	 * @param os 输出流
	 * @throws IOException 出错时抛出异常
	 */
	public void writeObject(HSSFWorkbook workbook, OutputStream os) throws IOException {

		try {
			// 创建Excel文件
			this.exportCellFormat = new ExportCellFormat(this.templateFile);
			this.initWorkbook();
			this.writeLine(writableWorkbook.getSheetName(0), 0, false);
			writableWorkbook.write(os);
			os.flush();
		} catch (Exception e) {
			LOGGER.error("ExportPrinter输出Excel出错：" + e.getMessage());
		} finally {
			if (os != null) {
				os.close();
				os = null;
			}
		}
	}
	/**
	 * 输出Excel
	 * 
	 * @param workbook 模板workbook
	 * @param os 输出流
	 * @throws IOException 出错时抛出异常
	 */
	public void writeObject(HSSFWorkbook workbook, HttpServletResponse response) throws IOException {

		try {
			// 创建Excel文件
			this.exportCellFormat = new ExportCellFormat(this.templateFile);
			this.initWorkbook();
			this.writeLine(writableWorkbook.getSheetName(0), 0, false);
			writableWorkbook.write(response.getOutputStream());
			response.getOutputStream().flush();
		} catch (Exception e) {
			LOGGER.error("ExportPrinter输出Excel出错：" + e.getMessage());
		} finally {
			if (response.getOutputStream() != null) {
				response.getOutputStream().close();
			}
		}
	}

	/**
	 * 输出Excel
	 * 
	 * @param workbook 模板workbook
	 * @param os 输出流
	 * @throws IOException 出错时抛出异常
	 */
	public void writeDynaObject(HSSFWorkbook workbook) throws IOException {
		FileOutputStream objFileOutputStream = null;
		try {
			// 创建临时文件夹
			File objTempFile = new File(this.filePath);
			if (!objTempFile.exists()) {
				objTempFile.mkdirs();
			}
			objFileOutputStream = new FileOutputStream(this.filePath + this.fileName
					+ ExcelExportConstants.EXPORT_EXCEL_2003);
			workbook.write(objFileOutputStream);
		} catch (Exception e) {
			LOGGER.error("ExportPrinter输出Excel出错：" + e.getMessage());
		} finally {
			if (objFileOutputStream != null) {
				objFileOutputStream.close();
				objFileOutputStream = null;
			}
		}
	}

	/**
	 * 输出数据
	 * 
	 * @param sheetName 分页名
	 * @param sequence 次序
	 * @param bFlag 是否由模板导出
	 * @throws IOException 出错时抛出异常
	 */
	private void writeLine(String sheetName, int sequence, boolean bFlag) throws IOException {
		if (data != null && data.size() > sequence && data.get(sequence) != null) {
			Object objData = data.get(sequence);
			doMapWrite(sheetName, objData);
		}
	}

	/**
	 * 读取目标Excel文件
	 * 
	 * @return 目标Excel文件字节数组
	 */
	public byte[] write() {
		byte[] byteInBytes = null;
		FileInputStream objFileInputStream = null;
		try {
			objFileInputStream = new FileInputStream(this.filePath + this.fileName
					+ ExcelExportConstants.EXPORT_EXCEL_2003);
			byteInBytes = new byte[objFileInputStream.available()];
			objFileInputStream.read(byteInBytes);
		} catch (Exception e) {
			LOGGER.error("读写Excel文件时发生异常", e);
		} finally {
			try {
				if (objFileInputStream != null) {
					objFileInputStream.close();
				}
			} catch (IOException e) {
				LOGGER.error("关闭文件输入流时发生异常", e);
			}
			if (exportCellFormat != null) {
				exportCellFormat.clearFormat();
			}
			// 创建临时文件夹
			File objTempFile = new File(this.filePath);

			// 删除临时文件夹
			if (objTempFile.exists()) {
				deleteFile(objTempFile);
				objTempFile.delete();
			}
		}
		return byteInBytes;
	}

	/**
	 * 初始化writableWorkbook信息
	 * 
	 * @throws IOException 文件读写失败异常
	 */
	private void initWorkbook() throws IOException {
		// 创建临时文件夹
		File objTempFile = new File(this.filePath);
		if (!objTempFile.exists()) {
			objTempFile.mkdirs();
		}

		FileOutputStream objFileOutputStream = null;
		FileInputStream objFileIn = null;
		try {
			objFileOutputStream = new FileOutputStream(this.filePath + this.fileName
					+ ExcelExportConstants.EXPORT_EXCEL_2003);
			// 模板文件
			objFileIn = new FileInputStream(TEMPLATES_DIRECTORY + this.templateFile);
			copyStream(objFileIn, objFileOutputStream);
		} catch (IOException e) {
			throw new IOException(e);
		} finally {
			if (null != objFileIn) {
				objFileIn.close();
			}
			if (null != objFileOutputStream) {
				objFileOutputStream.close();
			}
		}

		FileInputStream objFin = new FileInputStream(this.filePath + this.fileName
				+ ExcelExportConstants.EXPORT_EXCEL_2003);
		POIFSFileSystem objFS = new POIFSFileSystem(objFin);
		writableWorkbook = new HSSFWorkbook(objFS);
		if(objTempFile!=null)
			FileUtils.deleteDirectory(objTempFile);

	}

	/**
	 * 输出使用模板定义的数据
	 * 
	 * @param sheetName 分页名
	 * @param objData 数据
	 */
	@SuppressWarnings("unchecked")
	private void doMapWrite(String sheetName, Object objData) {
		Map<String, Object> objDataMap = (Map<String, Object>) objData;
		List<String> lstTag = new ArrayList<String>();
		// 如果以后标签有扩展了这里就要添加相应的标签名称.
		lstTag.add("object");
		lstTag.add("iterator");
		lstTag.add("sum");
		int iId;
		List<Object> lstDataList;
		List<Object> lstTempList;
		TageObject objTagObject;
		TageList objTagList;
		TageSum objSum;
		writableSheet = writableWorkbook.getSheet(sheetName);
		Map<String, List<ExcelCell>> objAllTag = ExcelUtil.getAllTag(writableSheet, lstTag);
		pageSize = 40000;
		String strDataName = "list";// String.valueOf(breakMap.get(sheetName));
		int iPageCount = getPageCount(objDataMap, strDataName, pageSize);
		if (iPageCount <= 1) {
			objTagObject = new TageObject();
			objTagObject.setValue(writableSheet, objDataMap, objAllTag.get("object"));
			objTagList = new TageList();
			objTagList.setValue(writableSheet, objDataMap, objAllTag.get("iterator"));
			objSum = new TageSum();
			objSum.setValue(writableSheet, objDataMap, objAllTag.get("sum"));
		} else {
			lstDataList = (List<Object>) objDataMap.get(strDataName);
			lstTempList = new ArrayList<Object>();
			HSSFSheet objNewSheet;

			for (int i = 0; i < iPageCount; i++) {
				objNewSheet = clone(sheetName, i + 1);
				lstTempList.clear();
				for (int j = 0; j < pageSize; j++) {
					iId = i * pageSize + j;
					if (iId < lstDataList.size()) {
						lstTempList.add(lstDataList.get(iId));
					}
				}
				objDataMap.put(strDataName, lstTempList);

				objTagObject = new TageObject();
				objTagObject.setValue(objNewSheet, objDataMap, objAllTag.get("object"));
				objTagList = new TageList();
				objTagList.setValue(objNewSheet, objDataMap, objAllTag.get("iterator"));
				objSum = new TageSum();
				objSum.setValue(writableSheet, objDataMap, objAllTag.get("sum"));
			}
			removeSheet();
		}
	}

	/**
	 * 根据数据源中数据的多少和分页的每页的记录数算出分页的页数
	 * 
	 * @param dataMap 数据集合
	 * @param dataName sheet页
	 * @param size 每页显示数据量
	 * @return sheet页
	 */
	@SuppressWarnings("unchecked")
	private int getPageCount(Map<String, Object> dataMap, String dataName, int size) {
		List<Object> lstData = (List<Object>) dataMap.get(dataName);
		return (int) Math.ceil((float) lstData.size() / size);
	}

	/**
	 * 用于在分页的时候根据模板sheet创建类似的sheet
	 * 
	 * @param templateSheetName 模板sheet名
	 * @param iCount sheet页
	 * @return sheet页
	 */
	private HSSFSheet clone(String templateSheetName, int iCount) {
		HSSFSheet objWritableSheet = null;
		int iTemplateSheetIndex = writableWorkbook.getSheetIndex(templateSheetName);
		if (iTemplateSheetIndex > -1) {
			objWritableSheet = writableWorkbook.cloneSheet(iTemplateSheetIndex);
			int iCloneIndex = writableWorkbook.getNumberOfSheets() - 1;
			writableWorkbook.setSheetName(iCloneIndex, templateSheetName + "(" + iCount + ")");
			this.delSheetName.add(templateSheetName);
		}
		return objWritableSheet;
	}

	/**
	 * 删掉excel中的模板sheet
	 */
	private void removeSheet() {
		Iterator<String> objIterator = this.delSheetName.iterator();
		int iTemplateIndex;
		String strTemplateName;
		while (objIterator.hasNext()) {
			strTemplateName = objIterator.next();
			iTemplateIndex = writableWorkbook.getSheetIndex(strTemplateName);
			writableWorkbook.removeSheetAt(iTemplateIndex);
		}
	}

	/**
	 * 在指定单元格里写数据
	 * 
	 * @param sheet 分页信息
	 * @param row int 行数
	 * @param col int 列数
	 * @param xlsData String 数据内容
	 * @param objHSSFCellStyle 单元格 样式
	 * @param width 宽度
	 * @throws OfficeException 出错时抛出异常
	 */
	public void writeData(HSSFSheet sheet, int row, int col, String xlsData,
			HSSFCellStyle objHSSFCellStyle, String width) throws ExportExcelException {
		HSSFRow objRow = sheet.getRow(row);
		if (objRow == null) {
			objRow = sheet.createRow(row);
		}
		HSSFCell objCell = objRow.getCell(col);
		if (objCell == null) {
			objCell = objRow.createCell(col);
		}
		// objCell.setEncoding(HSSFCell.ENCODING_UTF_16);
		objCell.setCellStyle(objHSSFCellStyle);
		objCell.setCellValue(xlsData);
		if (width != null && !"".equals(width)) {
			sheet.setColumnWidth(col, (short) (Integer.parseInt(width) * 500));
		}
	}

	/**
	 * 将输入流写到输出流中
	 * 
	 * @param in in
	 * @param out out
	 * @throws IOException IOException
	 */
	private void copyStream(InputStream in, OutputStream out) throws IOException {
		byte[] byteChunk = new byte[1024];
		int iCount;
		while ((iCount = in.read(byteChunk)) >= 0) {
			out.write(byteChunk, 0, iCount);
		}
	}

	/**
	 * 删除临时文件件
	 * 
	 * @param tempFile 临时文件件
	 */
	private void deleteFile(File tempFile) {
		if (tempFile.isDirectory()) {
			File[] objFiles = tempFile.listFiles();
			for (File objFile : objFiles) {
				deleteFile(objFile);
			}
		} else {
			tempFile.delete();
		}
	}
}
